package com.lianqi.emcpframework.ddd.gencode.controller.writer;

import static com.lianqi.emcpframework.ddd.gencode.controller.writer.PathUtils.getPathFromMethod;
import static com.lianqi.emcpframework.ddd.gencode.utils.MethodUtils.createParamListStr;

import java.util.List;

import javax.lang.model.element.Modifier;

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;

import com.lianqi.emcpframework.ddd.gencode.Description;
import com.lianqi.emcpframework.ddd.gencode.controller.GenControllerAnnotationParser;
import com.lianqi.emcpframework.ddd.gencode.controller.GenControllerMethodMeta;
import com.lianqi.emcpframework.ddd.gencode.controller.request.CreateMethodRequestBodyParser;
import com.lianqi.emcpframework.ddd.gencode.controller.request.RequestBodyInfo;
import com.lianqi.emcpframework.ddd.gencode.define.JavaSource;
import com.lianqi.emcpframework.ddd.gencode.define.TypeCollector;
import com.squareup.javapoet.*;

import io.swagger.annotations.ApiOperation;

public final class GenControllerCreateMethodWriter extends GenControllerMethodWriterSupport {

    public GenControllerCreateMethodWriter(GenControllerAnnotationParser parser,
        List<GenControllerMethodMeta.MethodMeta> methods, TypeCollector typeCollector) {
        super(parser, methods, typeCollector);
    }

    @Override
    protected void writeMethod(GenControllerMethodMeta.MethodMeta executableElement, JavaSource typeBuilder) {
        String methodName = executableElement.getMethodName();
        Description description = executableElement.getDescription();
        String descriptionStr = description != null ? description.value() : "";
        MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder(methodName).addModifiers(Modifier.PUBLIC)
            .addAnnotation(ResponseBody.class)
            .addAnnotation(AnnotationSpec.builder(ApiOperation.class).addMember("value", "\"" + descriptionStr + "\"")
                .addMember("nickname", "\"" + methodName + "\"").build())
            .addAnnotation(AnnotationSpec.builder(PostMapping.class)
                .addMember("value", "\"/" + getPathFromMethod(methodName) + "\"").build());

        RequestBodyInfo requestBodyInfo = new CreateMethodRequestBodyParser(getPkgName(), getBaseClassName())
            .parseForMethod(executableElement.getExecutableElement());

        requestBodyInfo.getBodyType().forEach(innerBuilder -> typeBuilder.addType(innerBuilder.build()));

        methodBuilder
            .addParameter(ParameterSpec.builder(requestBodyInfo.getParameterType(), requestBodyInfo.getParameterName())
                .addAnnotation(RequestBody.class).build());

        if (getParser().isWrapper()) {
            if ("void".equalsIgnoreCase(executableElement.getReturnType().toString())) {
                methodBuilder.addStatement("this.getApplication().$L($L)", executableElement.getMethodName(),
                    createParamListStr(requestBodyInfo.getCallParams()));
                methodBuilder.addStatement("return $T.success()", ClassName.bestGuess(getParser().getWrapperCls()));

                methodBuilder.returns(
                    ParameterizedTypeName.get(ClassName.bestGuess(getParser().getWrapperCls()), TypeName.VOID.box()));
            } else {
                methodBuilder.addStatement("return $T.success(this.getApplication().$L($L))",
                    ClassName.bestGuess(getParser().getWrapperCls()), executableElement.getMethodName(),
                    createParamListStr(requestBodyInfo.getCallParams()));

                methodBuilder.returns(ParameterizedTypeName.get(ClassName.bestGuess(getParser().getWrapperCls()),
                    TypeName.get(executableElement.getReturnType())));
            }

        } else {
            methodBuilder.addStatement("return this.getApplication().$L($L)", executableElement.getMethodName(),
                createParamListStr(requestBodyInfo.getCallParams()));

            methodBuilder.returns(TypeName.get(executableElement.getReturnType()));
        }

        typeBuilder.addMethod(methodBuilder.build());
    }

}
